from rest_framework import serializers
from .models import BillOfMaterials, BOMComponent, Product, AlternativeMaterial, BOMDocument, BOMVersion
from system.models import User
from .dict_utils import get_dict_label_by_value


class BOMVersionSerializer(serializers.ModelSerializer):
    """BOM版本序列化器"""
    approved_by_name = serializers.CharField(source='approved_by.username', read_only=True)
    created_by_name = serializers.CharField(source='created_by.username', read_only=True)
    status_display = serializers.SerializerMethodField(read_only=True)
    
    def get_status_display(self, obj):
        """获取版本状态显示名称"""
        from .dict_utils import BOM_VERSION_STATUS_DICT
        return get_dict_label_by_value(BOM_VERSION_STATUS_DICT, obj.status)
    
    class Meta:
        model = BOMVersion
        fields = [
            'version_id', 'bom', 'version_number', 'status', 'status_display', 'change_reason',
            'change_content', 'approved_by', 'approved_by_name', 'approved_at',
            'created_by', 'created_by_name', 'effective_date', 'expiry_date',
            'is_current', 'configuration_changes', 'create_time', 'update_time'
        ]

class AlternativeMaterialSerializer(serializers.ModelSerializer):
    """替代物料序列化器"""
    alt_material_name = serializers.CharField(source='alternative_material.name', read_only=True)
    alt_material_sku = serializers.CharField(source='alternative_material.sku', read_only=True)
    approval_status_display = serializers.SerializerMethodField(read_only=True)
    
    def get_approval_status_display(self, obj):
        """获取审批状态显示名称"""
        from .dict_utils import ALT_MATERIAL_STATUS_DICT
        return get_dict_label_by_value(ALT_MATERIAL_STATUS_DICT, obj.approval_status)
    
    class Meta:
        model = AlternativeMaterial
        fields = [
            'alt_material_id', 'alternative_material', 'alt_material_name', 'alt_material_sku',
            'substitution_ratio', 'priority', 'valid_from', 'valid_until',
            'compatibility_score', 'approval_required', 'is_active', 'approval_status', 'approval_status_display',
            'reason', 'notes', 'cost_impact', 'performance_impact',
            'create_time', 'update_time'
        ]


class BOMDocumentSerializer(serializers.ModelSerializer):
    """BOM文档序列化器"""
    creator_name = serializers.CharField(source='creator.username', read_only=True)
    doc_type_display = serializers.SerializerMethodField(read_only=True)
    
    def get_doc_type_display(self, obj):
        """获取文档类型显示名称"""
        from .dict_utils import DOCUMENT_TYPE_DICT
        return get_dict_label_by_value(DOCUMENT_TYPE_DICT, obj.doc_type)
    
    class Meta:
        model = BOMDocument
        fields = [
            'doc_id', 'doc_type', 'doc_type_display', 'doc_name', 'doc_path', 'doc_url',
            'version', 'description', 'creator', 'creator_name', 'create_time'
        ]


class BOMComponentSerializer(serializers.ModelSerializer):
    """BOM组件序列化器"""
    material_name = serializers.CharField(source='material.name', read_only=True)
    material_sku = serializers.CharField(source='material.sku', read_only=True)
    alternatives = AlternativeMaterialSerializer(many=True, read_only=True)
    documents = BOMDocumentSerializer(many=True, read_only=True)
    subcomponents = serializers.SerializerMethodField(read_only=True)
    material_type_display = serializers.SerializerMethodField(read_only=True)
    
    def get_subcomponents(self, obj):
        """递归获取子组件"""
        if obj.subcomponents.exists():
            return BOMComponentSerializer(obj.subcomponents.filter(is_deleted=False), many=True).data
        return []
    
    def get_material_type_display(self, obj):
        """获取物料类型显示名称"""
        from .dict_utils import MATERIAL_TYPE_DICT
        return get_dict_label_by_value(MATERIAL_TYPE_DICT, obj.material_type)
    
    class Meta:
        model = BOMComponent
        fields = [
            'component_id', 'bom', 'parent_component', 'material', 'material_name', 'material_sku', 
            'quantity', 'unit', 'usage_rate', 'scrap_rate', 'sequence',
            'unit_cost', 'total_cost', 'material_type', 'material_type_display', 'operation_code',
            'operation_name', 'workstation', 'lead_time', 'level', 'path',
            'preferred_supplier_id', 'supplier_part_number', 'procurement_type',
            'quality_standard', 'certification_requirements', 'attributes',
            'description', 'alternatives', 'documents', 'subcomponents',
            'create_time', 'update_time', 'is_deleted'
        ]


class BillOfMaterialsSerializer(serializers.ModelSerializer):
    """BOM主表序列化器"""
    product_name = serializers.CharField(source='product.name', read_only=True)
    product_sku = serializers.CharField(source='product.sku', read_only=True)
    creator_name = serializers.CharField(source='creator.username', read_only=True, allow_null=True)
    bom_type_display = serializers.SerializerMethodField(read_only=True)
    status_display = serializers.SerializerMethodField(read_only=True)
    lifecycle_status_display = serializers.SerializerMethodField(read_only=True)
    components = serializers.SerializerMethodField(read_only=True)
    documents = BOMDocumentSerializer(many=True, read_only=True)
    versions = BOMVersionSerializer(many=True, read_only=True)
    
    def get_bom_type_display(self, obj):
        """获取BOM类型显示名称"""
        from .dict_utils import BOM_TYPE_DICT
        return get_dict_label_by_value(BOM_TYPE_DICT, obj.bom_type)
    
    def get_status_display(self, obj):
        """获取状态显示名称"""
        from .dict_utils import BOM_STATUS_DICT
        return get_dict_label_by_value(BOM_STATUS_DICT, obj.status)
    
    def get_lifecycle_status_display(self, obj):
        """获取生命周期状态显示名称"""
        from .dict_utils import LIFECYCLE_STATUS_DICT
        return get_dict_label_by_value(LIFECYCLE_STATUS_DICT, obj.lifecycle_status)
    
    def get_components(self, obj):
        """只返回顶层组件，子组件通过递归获取"""
        return BOMComponentSerializer(obj.components.filter(parent_component__isnull=True, is_deleted=False), many=True).data
    
    class Meta:
        model = BillOfMaterials
        fields = [
            'bom_id', 'bom_code', 'product', 'product_name', 'product_sku', 'bom_type', 'bom_type_display',
            'current_version', 'effective_date', 'expiry_date', 'is_active', 'status', 'status_display',
            'lifecycle_status', 'lifecycle_status_display', 'cost', 'description', 'creator', 'creator_name',
            'create_time', 'update_time', 'configuration_template', 'routing_reference',
            'materialized_path', 'components', 'documents', 'versions', 'is_deleted'
        ]


class AlternativeMaterialCreateUpdateSerializer(serializers.ModelSerializer):
    """替代物料创建更新序列化器"""
    class Meta:
        model = AlternativeMaterial
        fields = [
            'alt_material_id', 'alternative_material', 'substitution_ratio',
            'priority', 'valid_from', 'valid_until',
            'compatibility_score', 'approval_required', 'is_active', 'approval_status',
            'reason', 'notes', 'cost_impact', 'performance_impact'
        ]


class BOMComponentCreateUpdateSerializer(serializers.ModelSerializer):
    """BOM组件创建更新序列化器"""
    alternatives = AlternativeMaterialCreateUpdateSerializer(many=True, required=False)
    
    class Meta:
        model = BOMComponent
        fields = [
            'component_id', 'parent_component', 'material', 'quantity', 'unit', 'usage_rate',
            'scrap_rate', 'sequence', 'unit_cost', 'total_cost', 'material_type',
            'operation_code', 'operation_name', 'workstation', 'lead_time',
            'preferred_supplier_id', 'supplier_part_number', 'procurement_type',
            'quality_standard', 'certification_requirements', 'attributes',
            'description', 'alternatives'
        ]
    
    def validate(self, data):
        """验证组件数据，防止循环引用"""
        component_id = data.get('component_id')
        parent_component = data.get('parent_component')
        
        # 防止自我引用
        if component_id and parent_component and component_id == parent_component.component_id:
            raise serializers.ValidationError("组件不能引用自身作为父组件")
        
        # 防止循环引用（简单验证）
        if parent_component:
            current = parent_component
            while current.parent_component:
                if component_id and current.parent_component.component_id == component_id:
                    raise serializers.ValidationError("检测到循环引用")
                current = current.parent_component
        
        return data


class BOMCreateUpdateSerializer(serializers.ModelSerializer):
    """BOM创建更新序列化器，支持嵌套组件数据和替代物料"""
    components = BOMComponentCreateUpdateSerializer(many=True)
    
    class Meta:
        model = BillOfMaterials
        fields = [
            'bom_id', 'bom_code', 'product', 'bom_type', 'current_version', 'effective_date', 
            'expiry_date', 'is_active', 'status', 'lifecycle_status', 'cost', 'description',
            'configuration_template', 'routing_reference', 'materialized_path', 'components'
        ]
    
    def create(self, validated_data):
        components_data = validated_data.pop('components')
        # 设置创建人
        validated_data['creator'] = self.context['request'].user
        
        # 如果没有提供BOM编码，则自动生成
        if 'bom_code' not in validated_data or not validated_data['bom_code']:
            product = validated_data['product']
            version = validated_data.get('current_version', '1.0')
            bom_code = f"BOM-{product.sku}-{version}"
            
            # 确保BOM编码唯一，如果重复则添加序号
            counter = 1
            original_bom_code = bom_code
            while BillOfMaterials.objects.filter(bom_code=bom_code, is_deleted=False).exists():
                bom_code = f"{original_bom_code}-{counter}"
                counter += 1
            
            validated_data['bom_code'] = bom_code
        
        bom = BillOfMaterials.objects.create(**validated_data)
        
        for component_data in components_data:
            alternatives_data = component_data.pop('alternatives', [])
            component = BOMComponent.objects.create(bom=bom, **component_data)
            
            # 创建替代物料
            for alt_data in alternatives_data:
                AlternativeMaterial.objects.create(component=component, **alt_data)
            
        # 计算总成本
        self.calculate_total_cost(bom)
        return bom
    
    def update(self, instance, validated_data):
        components_data = validated_data.pop('components')
        
        # 更新BOM主表信息
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        
        # 更新组件信息
        component_ids = []
        for component_data in components_data:
            alternatives_data = component_data.pop('alternatives', [])
            
            if 'component_id' in component_data:
                # 更新现有组件
                component = BOMComponent.objects.get(component_id=component_data['component_id'])
                for attr, value in component_data.items():
                    setattr(component, attr, value)
                component.save()
                
                # 更新替代物料
                alt_ids = []
                for alt_data in alternatives_data:
                    if 'alt_material_id' in alt_data:
                        # 更新现有替代物料
                        alt = AlternativeMaterial.objects.get(alt_material_id=alt_data['alt_material_id'])
                        for attr, value in alt_data.items():
                            setattr(alt, attr, value)
                        alt.save()
                        alt_ids.append(alt.alt_material_id)
                    else:
                        # 创建新替代物料
                        alt = AlternativeMaterial.objects.create(component=component, **alt_data)
                        alt_ids.append(alt.alt_material_id)
                
                # 删除未包含在更新数据中的替代物料
                component.alternative_material_set.exclude(alt_material_id__in=alt_ids).delete()
                
                component_ids.append(component.component_id)
            else:
                # 创建新组件
                component = BOMComponent.objects.create(bom=instance, **component_data)
                
                # 创建替代物料
                for alt_data in alternatives_data:
                    AlternativeMaterial.objects.create(component=component, **alt_data)
                
                component_ids.append(component.component_id)
        
        # 删除未包含在更新数据中的组件
        instance.components.exclude(component_id__in=component_ids).delete()
        
        # 计算总成本
        self.calculate_total_cost(instance)
        return instance
    
    def calculate_total_cost(self, bom):
        """计算BOM总成本"""
        total_cost = sum(component.total_cost or 0 for component in bom.components.all())
        bom.cost = total_cost
        bom.save()